import Flutter
import UIKit
import iOSDFULibrary
import CoreBluetooth
import SSZipArchive

public class NordicOtasPlugin: NSObject,FlutterPlugin,DFUServiceDelegate,DFUProgressDelegate,CBCentralManagerDelegate,CBPeripheralManagerDelegate{
    

    var filePath = ""
    var controller:DFUServiceController? = nil
    var selectedPeripheral:CBPeripheral? = nil
    var centralManager:CBCentralManager? = nil
    var selectedFirmware:DFUFirmware? = nil
    
    
    public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
//        print("search: \(peripheral.name)")
        if (peripheral.name != nil && peripheral.name!.contains("Dfu2301")) {
            selectedPeripheral = peripheral
            centralManager?.stopScan()
            setMessage(message: "2@0")
            self.startOta()
        }
    }

    public func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        
    }
    

    public func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
               case .unknown:
//                   print("CBCentralManager state:", "unknown")
                   break
               case .resetting:
//                   print("CBCentralManager state:", "resetting")
                   break
               case .unsupported:
//                   print("CBCentralManager state:", "unsupported")
                   break
               case .unauthorized:
//                   print("CBCentralManager state:", "unauthorized")
                   break
               case .poweredOff:
//                   print("CBCentralManager state:", "poweredOff")
                   break
               case .poweredOn:
//                   print("CBCentralManager state:", "poweredOn")
                   setMessage(message: "1@0")
                    self.centralManager!.scanForPeripherals(withServices: nil, options:nil)
               }
    }

    

    
    
    public func dfuProgressDidChange(for part: Int, outOf totalParts: Int, to progress: Int, currentSpeedBytesPerSecond: Double, avgSpeedBytesPerSecond: Double) {
//        print("dfuProgressDidChange: \(progress)")
        setMessage(message: "3@\(progress)")
    }
    
    
    public func dfuStateDidChange(to state: DFUState) {
        switch state {
        case .connecting:
            break
        case .starting:
            break
        case .enablingDfuMode:
            break
        case .uploading:
            break
        case .validating:
            break
        case .disconnecting:
            setMessage(message: "6@0")
            break
        case .completed:
            setMessage(message: "5@0")
            break
        case .aborted:
            break
        }
    }
    
    public func dfuError(_ error: DFUError, didOccurWithMessage message: String) {
//        print("dfuError")
        setMessage(message: "4@0")
    }
    
  public static func register(with registrar: FlutterPluginRegistrar) {
      UnzipFirmware.channel = FlutterMethodChannel(name: "nordic_otas_plugin", binaryMessenger: registrar.messenger())
    let instance = NordicOtasPlugin()
    registrar.addMethodCallDelegate(instance, channel:UnzipFirmware.channel!)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
//      print("method: \(call.method)")
      if call.method == "setFilePath" {
          let data:NSDictionary = call.arguments as! NSDictionary
          let path = data["path"] as! String
          setFilePath(path: path)
          
      } else if call.method == "startOta" {
          self.searchDevice()
      }
  }
    
    public func searchDevice(){
        self.centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
        
    }
    
    public func setMessage(message:String){
        if UnzipFirmware.channel != nil{
            print("setMessage:\(message)")
            UnzipFirmware.channel?.invokeMethod("sendMessage", arguments: message)
        }
    }
    

    
    public func setFilePath(path:String){
        self.filePath = path
        if !self.filePath.isEmpty {
            let manager = FileManager.default
            do {
                let documentsURL = try manager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                let newFolderURL = documentsURL.appendingPathComponent("UnFiles")
                try manager.createDirectory(at: newFolderURL, withIntermediateDirectories: true, attributes: nil)
//                print("文件夹创建成功")
                SSZipArchive.unzipFile(atPath: path, toDestination: newFolderURL.path)
                guard let files = manager.subpaths(atPath: newFolderURL.path)
                else{
                    return
                }
                for p in files {
                    print("value1: \(newFolderURL.path)/\(p)")
                    if(p.contains("zip")){
                        let pp = "\(newFolderURL.path)/\(p)"
                        let url = URL.init(fileURLWithPath: pp)
//                        print("value1:\(url.description)")
                        self.selectedFirmware = DFUFirmware.init(urlToZipFile: url)
                    }
                }
            } catch {
                setMessage(message: "7@0")
//                print("创建文件夹时发生错误: \(error)")
            }
        }
    }
    
    
    
    public func startOta(){
        if(self.centralManager == nil){
//            print("centralManager is nil")
            return
        }
        if(self.selectedPeripheral == nil){
//            print("selectedPeripheral is nil")
            return
        }
        if(self.selectedFirmware == nil){
//            print("selectedFirmware is nil")
            return
        }
        
        let initiator = DFUServiceInitiator(centralManager: centralManager!, target: selectedPeripheral!)
        initiator.with(firmware: selectedFirmware!)
        initiator.forceDfu = true
        initiator.delegate = self
        initiator.progressDelegate = self
        controller = initiator.start()
    }
    
    
}
